package com.xingda.packing.dmss.domain;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xingda.packing.dmss.common.PackUtils;
import com.xingda.packing.dmss.config.appset.AppSetParam;
import com.xingda.packing.dmss.events.EventCreater;
import com.xingda.packing.dmss.minitor.entity.PkCacheLocation;
import com.xingda.packing.dmss.minitor.entity.PkWheel;
import com.xingda.packing.dmss.minitor.mapper.PkCacheLocationMapper;
import com.xingda.packing.dmss.minitor.mapper.PkWheelMapper;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 缓存区
 */
@Data
@Slf4j
@Component
public class CacheArea implements Serializable {
    @Value("${com.xingda.cacheLocation.leftMaxNo}")
    @Getter
    private Integer leftMaxNo;
    @Value("${com.xingda.cacheLocation.rightMaxNo}")
    @Getter
    private Integer rightMaxNo;
    @Resource
    PkCacheLocationMapper pkCacheLocationMapper;

    @Resource
    PkWheelMapper pkWheelMapper;

    @Resource
    EventCreater eventCreater;

    private static final long serialVersionUID = -1962081149024744692L;

    /**
     * 缓存位列表
     */
    @Setter(AccessLevel.NONE)
    private List<CacheLocation> locationList1 = new ArrayList<>();

    @Setter(AccessLevel.NONE)
    private List<CacheLocation> locationList2 = new ArrayList<>();

    /**
     * 左侧B80最大位置号
     */
    int maxB80LocationLNo;
    /**
     * 右侧B80最大位置号
     */
    int maxB80LocationRNo;
    /**
     * 左侧B40/B60最大位置号
     */
    int maxB60LocationLNo;
    /**
     * 右侧B40/B80最大位置号
     */
    int maxB60LocationRNo;

    public List<CacheLocation> getLocationList(int area) {
        if (area == 1) {
            return locationList1;
        } else {
            return locationList2;
        }
    }

    public int getMaxB80Num(int area) {
        if (area == 1) {
            return maxB80LocationLNo;
        } else {
            return maxB80LocationRNo;
        }
    }

    public int getMaxB60Num(int area) {
        if (area == 1) {
            return maxB60LocationLNo;
        } else {
            return maxB60LocationRNo;
        }
    }

    public CacheArea() {
    }

    /**
     * 添加缓存位置
     */
    public void addLocation(int locationNo, Integer enable, int area) {
        CacheLocation location = new CacheLocation();
        location.setNo(locationNo);
        location.setEnable(enable);
        if (area == 1) {
            this.locationList1.add(location);
        } else {
            this.locationList2.add(location);
        }
    }

    /**
     * 无节轮缓存区是否已满
     *
     * @return 满=true,未满=false
     */
    public boolean isFull(int area) {
        return canPutCount(area) <= AppSetParam.cache_size;
    }

    /**
     * 可以放无节轮的缓存位个数
     */
    public int canPutCount(int area) {
        if (area == 1) {
            return maxB60LocationLNo - maxB80LocationLNo;
        } else {
            return maxB60LocationRNo - maxB80LocationRNo;
        }
    }


    /**
     * 指定位置放轮
     *
     * @param locationNo 缓存位置编号
     * @param wheel      工字轮
     */
    public void putWheel(int locationNo, Wheel wheel, int area) {
        putWheel(locationNo, wheel, LocalDateTime.now(), area);
    }

    /**
     * 指定位置放轮，放入时间可指定 用于从数据库恢复数据
     *
     * @param locationNo 缓存位置Id
     * @param wheel      工字轮
     * @param dateTime   放置时间
     */
    public void putWheel(int locationNo, Wheel wheel, LocalDateTime dateTime, int area) {
        Optional<CacheLocation> cacheLocation = getLocation(locationNo, area);
        if (cacheLocation.isPresent()) {
            CacheLocation location = cacheLocation.get();
            // 2024年10月6日 如果位置被占用，并且不是当前放入的轮，则放不进去
            if (location.isOccupy()) {
                if (!Objects.equals(wheel.getBarCode(), location.getWheel().getBarCode())) {
                    log.warn("缓存区:{}重复放轮:{}", locationNo, wheel.getBarCode());
                } else {
                    log.error("缓存区放轮失败：位置{}已被占用,占用轮号{}", locationNo, location.getWheel().getBarCode());
                }
            } else {
                location.setPutTime(dateTime);
                location.setWheel(wheel);
                int wheelTypeCode = location.getProductType();
                if (wheelTypeCode == 1 || wheelTypeCode == 3) {
                    if (area > 1) {
                        if (locationNo < maxB60LocationRNo) {
                            maxB60LocationRNo = locationNo;
                            log.info("右侧缓存区B60/B40最大位置号{}", maxB60LocationRNo);
                        }
                    } else {
                        if (locationNo < maxB60LocationLNo) {
                            maxB60LocationLNo = locationNo;
                            log.info("左侧缓存区B60/B40最大位置号{}", maxB60LocationLNo);
                        }
                    }
                }
                if (wheelTypeCode == 2 || wheelTypeCode == 4) {
                    if (area > 1) {
                        if (locationNo > maxB80LocationRNo) {
                            maxB80LocationRNo = locationNo;
                            log.info("右侧缓存区B80最大位置号{}", maxB80LocationRNo);
                        }
                    } else {
                        if (locationNo > maxB80LocationLNo) {
                            maxB80LocationLNo = locationNo;
                            log.info("左侧缓存区B80最大位置号{}", maxB80LocationLNo);
                        }
                    }
                }
                log.info("缓存区放轮成功：轮号{},扭转值{},类型:{},缓存位置{},放置时间{}", wheel.getBarCode(), wheel.getTorsionValue(), wheel.getAb(), locationNo, dateTime);
            }
        } else {
            log.error("缓存区放轮失败：位置{}不存在", locationNo);
        }
    }

    /**
     * 根据缓存位置拿轮
     *
     * @param locationNo 缓存位置Id
     */
    public void takeOutWheel(int locationNo, int area) {
        Optional<CacheLocation> cacheLocation = getLocation(locationNo, area);
        if (cacheLocation.isPresent()) {
            CacheLocation location = cacheLocation.get();
            if (location.isOccupy()) {
                log.info("缓存区取轮成功，位置：{},扭转值：{},类型:{}", locationNo, location.getWheel().getTorsionValue(), location.getWheel().getAb());
                location.clear();
            } else {
                log.error("缓存区拿轮失败：位置{}为空", locationNo);
            }
        } else {
            log.error("缓存区拿轮失败：位置{}不存在", locationNo);
        }
    }

    public void removeWheel(int locationNo, int area) {
        Optional<CacheLocation> cacheLocation = getLocation(locationNo, area);
        if (cacheLocation.isPresent()) {
            CacheLocation location = cacheLocation.get();
            if (location.isOccupy()) {
                log.info("缓存区取轮成功，位置：{},扭转值：{},类型:{}", locationNo, location.getWheel().getTorsionValue(), location.getWheel().getAb());
                getLocationList(area).clear();
                initStatus(area);
            } else {
                log.error("缓存区拿轮失败：位置{}为空", locationNo);
            }
        } else {
            log.error("缓存区拿轮失败：位置{}不存在", locationNo);
        }
    }

    /**
     * 获取缓存区可放轮位置号
     */
    public Optional<CacheLocation> nextPutLocationNo(int wheelTypeCode, int area) {
        //1B60 2B80 3B40 4其他
        if (area == 1) {
            if (wheelTypeCode == 1) {
                return locationList1.stream().filter(CacheLocation::canPut).max(Comparator.comparing(CacheLocation::getNo));
            }
            return locationList1.stream().filter(CacheLocation::canPut).min(Comparator.comparing(CacheLocation::getNo));
        } else {
            if (wheelTypeCode == 1) {
                return locationList2.stream().filter(CacheLocation::canPut).max(Comparator.comparing(CacheLocation::getNo));
            }
            return locationList2.stream().filter(CacheLocation::canPut).min(Comparator.comparing(CacheLocation::getNo));
        }
    }

    /**
     * 获取缓存位置
     *
     * @param locationNo 缓存位置编号
     */
    public Optional<CacheLocation> getLocation(int locationNo, int area) {
        if (area > 1) {
            return locationList2.stream().filter(location -> location.getNo() == locationNo).findFirst();
        } else {
            return locationList1.stream().filter(location -> location.getNo() == locationNo).findFirst();
        }

    }

    /**
     * 初始化加载缓存位信息
     */
    public void initStatus(int area) {
        initMaxLo(area);
        LambdaQueryWrapper<PkCacheLocation> pkCacheLocationQueryWrapper = Wrappers.lambdaQuery();
        if (area == 1) {
            pkCacheLocationQueryWrapper.between(PkCacheLocation::getId, 1, leftMaxNo);
        } else {
            pkCacheLocationQueryWrapper.between(PkCacheLocation::getId, leftMaxNo + 1, leftMaxNo + rightMaxNo);
        }
        List<PkCacheLocation> list = pkCacheLocationMapper.selectList(pkCacheLocationQueryWrapper);
        if (Objects.isNull(list) || list.isEmpty()) {
            log.error("缓存位未配置位置！");
            return;
        }
        for (PkCacheLocation pkCacheLocation : list) {
            Integer locationNo = pkCacheLocation.getLocationNo();
            Integer enable = pkCacheLocation.getEnable();
            this.addLocation(locationNo, enable, area);
            if (Objects.nonNull(pkCacheLocation.getWheelId())) {
                PkWheel pkWheel = pkWheelMapper.selectById(pkCacheLocation.getWheelId());
                Wheel wheel = PackUtils.toWheel(pkWheel);
                LocalDateTime ldt = pkWheel.getScanTime().toInstant().atZone(ZoneId.of("Asia/Shanghai")).toLocalDateTime();
                this.putWheel(locationNo, wheel, ldt, area);
            }
        }
    }

    public void initMaxLo(int area) {
        if (area == 1) {
            maxB80LocationLNo = 1;
            maxB60LocationLNo = leftMaxNo;
        } else {
            maxB80LocationRNo = 1;
            maxB60LocationRNo = rightMaxNo;
        }
    }

    /**
     * 获取已经缓存位工字轮
     */
    public List<CacheLocation> getOccupyLocation(int area) {
        return getLocationList(area).stream().filter(CacheLocation::isOccupy).collect(Collectors.toList());
    }
}
